package com.feather.common.converter.simple;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.feather.common.converter.ArrayIndex;
import com.feather.common.converter.FieldAssignmentHelper;
import com.feather.common.converter.NameMatch;
import com.feather.common.converter.ObjectAssignment;

/**
 * @author flogyin
 */
public class SimpleObjectAssignment implements ObjectAssignment {
	private boolean					caseMatch;
	private FieldAssignmentHelper	helper;
	private ArrayIndex				arrayindex;

	public SimpleObjectAssignment() {
	}

	public SimpleObjectAssignment(boolean caseMatch, FieldAssignmentHelper helper) {
		this.caseMatch = caseMatch;
		this.helper = helper;
	}

	public SimpleObjectAssignment(boolean caseMatch, FieldAssignmentHelper helper, ArrayIndex arrayindex) {
		this.caseMatch = caseMatch;
		this.helper = helper;
		this.arrayindex = arrayindex;
	}

	@Override
	public boolean isCaseMatch() {
		return caseMatch;
	}

	@Override
	public void setCaseMatch(boolean match) {
		this.caseMatch = match;
	}

	@Override
	public FieldAssignmentHelper getFieldAssignmentHelper() {
		return this.helper;
	}

	@Override
	public void setFieldAssignmentHelper(FieldAssignmentHelper helper) {
		this.helper = helper;
	}

	@SuppressWarnings("rawtypes")
	public boolean foTarget(String prefix, Class cls, Object tar, List<String> keys, List<Object> values, int size, List<String> matchFieldList) throws IllegalAccessException {
		boolean mapped = false;

		Field[] fields = cls.getDeclaredFields();
		if (fields == null) {
			return mapped;
		}

		for (int i = 0; i < fields.length; i++) {
			if (Modifier.isFinal(fields[i].getModifiers())) {
				continue;
			}
			fields[i].setAccessible(true);
			String fieldName = fields[i].getName();
			for (int k = 0; k < size; k++) {
				NameMatch nameMatch = SimpleNameMatch.getNameMatch(this.caseMatch, prefix);
				int code = nameMatch.match(prefix, keys.get(k), fieldName, this.arrayindex);
				if (code == NameMatch.NOT_MATCH) {
					continue;
				}
				if (matchFieldList != null) {
					matchFieldList.add(fieldName);
				}

				this.helper.assign(values.get(k), tar, fields[i]);
				keys.remove(k);
				values.remove(k);
				// k--;
				// size--;
				mapped = true;
				break;
			}
		}

		Class superClass = cls.getSuperclass();
		if (!superClass.equals(Object.class)) {
			boolean m = foTarget(prefix, superClass, tar, keys, values, size, matchFieldList);
			if (m)
				mapped = m;
		}
		return mapped;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public int foTargetArray(String prefix, Class cls, List<String> keys, List<Object> values, int size, Map valueList) throws Exception {
		int maxIndex = -1;

		Field[] fields = cls.getDeclaredFields();
		if (fields == null) {
			return maxIndex;
		}

		for (int i = 0; i < fields.length; i++) {
			if (Modifier.isFinal(fields[i].getModifiers())) {
				continue;
			}
			fields[i].setAccessible(true);
			String fieldName = fields[i].getName();
			for (int k = 0; k < size; k++) {
				NameMatch nameMatch = SimpleNameMatch.getNameMatch(this.caseMatch, prefix, this.arrayindex);
				int code = nameMatch.match(prefix, keys.get(k), fieldName, this.arrayindex);
				if (code == NameMatch.NOT_MATCH) {
					continue;
				}

				if (maxIndex < code) {
					maxIndex = code;
				}
				Object obj = valueList.get(code);
				if (obj == null) {
					obj = cls.newInstance();
					valueList.put(code, obj);
				}

				this.helper.assign(values.get(k), obj, fields[i]);
				// k--;
				// size--;
				// break;
			}
		}

		Class superClass = cls.getSuperclass();
		if (!superClass.equals(Object.class)) {
			int mi = foTargetArray(prefix, superClass, keys, values, size, valueList);
			if (mi > maxIndex)
				maxIndex = mi;
		}

		return maxIndex;
	}

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Object get(Class cls, Map<String, Object> typeValueMap, String prefix) throws Exception {
		int size = typeValueMap.size();
		if (size == 0) {
			return null;
		}
		Map.Entry[] mapEntrys = new Map.Entry[size];
		typeValueMap.entrySet().toArray(mapEntrys);

		ArrayList<String> keys = new ArrayList<String>(size);
		ArrayList values = new ArrayList(size);
		for (int i = 0; i < size; i++) {
			keys.add((String) mapEntrys[i].getKey());
			values.add(mapEntrys[i].getValue());
		}

		Object newBean = cls.newInstance();
		boolean mapped = foTarget(prefix, cls, newBean, keys, values, size, null);
		if (mapped) {
			return newBean;
		}
		return null;
	}

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List<String> get(Object tar, Map<String, Object> typeValueMap, String prefix) throws IllegalAccessException {
		List<String> matchFieldList = new ArrayList<String>();

		int size = typeValueMap.size();
		if (size == 0) {
			return matchFieldList;
		}
		Map.Entry[] mapEntrys = new Map.Entry[size];
		typeValueMap.entrySet().toArray(mapEntrys);

		ArrayList<String> keys = new ArrayList<String>(size);
		ArrayList values = new ArrayList(size);
		for (int i = 0; i < size; i++) {
			keys.add((String) mapEntrys[i].getKey());
			values.add(mapEntrys[i].getValue());
		}

		foTarget(prefix, tar.getClass(), tar, keys, values, size, matchFieldList);
		return matchFieldList;
	}

	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object[] array(Class cls, Map<String, Object> typeValueMap, String prefix) throws Exception {
		int size = typeValueMap.size();
		if (size == 0) {
			return null;
		}
		Map.Entry[] mapEntrys = new Map.Entry[size];
		typeValueMap.entrySet().toArray(mapEntrys);

		ArrayList<String> keys = new ArrayList<String>(size);
		ArrayList values = new ArrayList(size);
		for (int i = 0; i < size; i++) {
			keys.add((String) mapEntrys[i].getKey());
			values.add(mapEntrys[i].getValue());
		}

		HashMap valueList = new HashMap();
		int maxIndex = foTargetArray(prefix, cls, keys, values, size, valueList);

		if (maxIndex > -1) {
			Object[] listBean = (Object[]) Array.newInstance(cls, maxIndex + 1);
			for (int i = 0; i <= maxIndex; i++) {
				listBean[i] = valueList.get(i);
			}
			return listBean;
		}
		return null;
	}
}
